﻿using System;
using System.Collections.Generic;
using System.Linq;
using Actya.Core.Extensibility;
using Actya.Core.Util;
using Actya.Core.Widgets;
using Newtonsoft.Json;

namespace Actya.Core.SiteStructure
{
	public class Site
	{
		private IEnumerable<SitemapNode> _flattenedNodes;

		public string Id { get; set; }
		public string Name { get; set; }
		public string DefaultTheme { get; set; }

		public List<string> HostUrls { get; set; } 
		public Dictionary<string, string> Settings { get; private set; }
		public List<PageType> PageTypes { get; private set; }
		public List<WidgetType> WidgetTypes { get; private set; }
		public List<Theme> Themes { get; private set; }
		public List<PageLayout> PageLayouts { get; private set; }
		public List<SitemapNode> SitemapNodes { get; set; }
		public Dictionary<string, string[]> AllowedMimeTypes { get; set; }
		public List<PluginType> PluginTypes { get; private set; }

		[JsonIgnore]
		public IEnumerable<SitemapNode> AllSitemapNodes
		{
			get { return _flattenedNodes ?? (_flattenedNodes = this.SitemapNodes.SelectDeep(n => n.SitemapNodes)); }
		}

		public Site()
		{
			this.HostUrls = new List<string>();
			this.Settings = new Dictionary<string, string>();
			this.PageTypes = new List<PageType>();
			this.WidgetTypes = new List<WidgetType>();
			this.Themes = new List<Theme>();
			this.PageLayouts = new List<PageLayout>();
			this.SitemapNodes = new List<SitemapNode>();
			this.AllowedMimeTypes = new Dictionary<string, string[]>();
			this.PluginTypes = new List<PluginType>();
		}

		public void Setup()
		{
			// Page types
			this.PageTypes.Add(new PageType { Id = PageTypeNames.HomePage, DisplayName = "Home page", Controller = "Page", Action = "Home", ContentModelType = ContentModelTypeNames.MultiRegionContent });
			this.PageTypes.Add(new PageType { Id = PageTypeNames.Page, DisplayName = "Standard page", Controller = "Page", Action = "Page", ContentModelType = ContentModelTypeNames.SimpleContent });
			this.PageTypes.Add(new PageType { Id = PageTypeNames.NotFound, DisplayName = "Not found error page", Controller = "Page", Action = "NotFound" });

			// System widget types
 			this.WidgetTypes.Add(new WidgetType { Name = WidgetTypeNames.Html, Description = "Displays arbitrary html or text.", WidgetModelType = WidgetModelTypeNames.HtmlWidget, IsSystem = true });
			this.WidgetTypes.Add(new WidgetType { Name = WidgetTypeNames.Twitter, Description = "Displays the last tweets of a user.", WidgetModelType = WidgetModelTypeNames.TwitterWidget, IsSystem = true });

			// Layouts
			this.PageLayouts.Add(new PageLayout
			                     	{
			                     		Id = Guid.NewGuid().ToString(),
			                     		Name = LayoutNames.BasicPage,
			                     		PageType = PageTypeNames.Page
			                     	});
			this.PageLayouts.Add(new PageLayout
			                     	{
										Id = Guid.NewGuid().ToString(),
			                     		Name = LayoutNames.HomePage,
			                     		PageType = PageTypeNames.HomePage
			                     	});

			// MIME types
			this.AllowedMimeTypes.Add("image/jpeg", new [] { "jpg","jpeg","jpe" });
			this.AllowedMimeTypes.Add("image/gif", new[] { "gif" });
			this.AllowedMimeTypes.Add("image/png", new[] { "png" });
			this.AllowedMimeTypes.Add("video/asf", new[] { "asf","asx","wax","wmv","wmx" });
			this.AllowedMimeTypes.Add("video/x-ms-wmv", new[] { "wmv" });
			this.AllowedMimeTypes.Add("video/avi", new[] { "avi" });
			this.AllowedMimeTypes.Add("video/x-flv", new[] { "flv" });
			this.AllowedMimeTypes.Add("video/quicktime", new[] { "mov","qt" });
			this.AllowedMimeTypes.Add("video/mpeg", new[] { "mpeg","mpg", "mpe" });
			this.AllowedMimeTypes.Add("text/plain", new[] { "txt" });
			this.AllowedMimeTypes.Add("text/csv", new[] { "csv" });
			this.AllowedMimeTypes.Add("text/css", new[] { "css" });
			this.AllowedMimeTypes.Add("text/html", new[] { "html","htm" });
			this.AllowedMimeTypes.Add("audio/mpeg", new[] { "mp3","m4a","m4b" });
			this.AllowedMimeTypes.Add("video/mp4", new[] { "mp4","m4v" });
			this.AllowedMimeTypes.Add("audio/wav", new[] { "wav" });
			this.AllowedMimeTypes.Add("audio/ogg", new[] { "ogg","oga" });
			this.AllowedMimeTypes.Add("audio/wma", new[] { "wma" });
			this.AllowedMimeTypes.Add("application/rtf", new[] { "rtf" });
			this.AllowedMimeTypes.Add("application/javascript", new[] { "js" });
			this.AllowedMimeTypes.Add("application/pdf", new[] { "pdf" });
			this.AllowedMimeTypes.Add("application/msword", new[] { "doc","docx" });
			this.AllowedMimeTypes.Add("application/vnd.ms-powerpoint", new[] { "pot","pps","ppt","pptx" });
			this.AllowedMimeTypes.Add("application/vnd.ms-excel", new[] { "xla","xls","xlsx","xlt" });
			this.AllowedMimeTypes.Add("application/vnd.ms-access", new[] { "mdb" });
			this.AllowedMimeTypes.Add("application/vnd.visio", new[] { "vsd","vst", "vss", "vsw" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.presentationml.presentation", new[] { "pptx" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", new[] { "sldx", "ppsx" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.presentationml.template", new[] { "potx" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", new[] { "xlsx" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.spreadsheetml", new[] { "xltx" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", new[] { "docx" });
			this.AllowedMimeTypes.Add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", new[] { "dotx" });
			this.AllowedMimeTypes.Add("application/x-shockwave-flash", new[] { "swf" });
			this.AllowedMimeTypes.Add("application/x-tar", new[] { "tar" });
			this.AllowedMimeTypes.Add("application/zip", new[] { "zip" });
			this.AllowedMimeTypes.Add("application/x-gzip", new[] { "zip","gz" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.text", new[] { "odt" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.presentation", new[] { "odp" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.spreadsheet", new[] { "ods" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.graphics", new[] { "odg" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.chart", new[] { "odc" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.database", new[] { "odb" });
			this.AllowedMimeTypes.Add("application/vnd.oasis.opendocument.formula", new[] { "odf" });
		}

		public PageType GetPageTypeById(string pageTypeId)
		{
			return PageTypes.Single(pt => pt.Id == pageTypeId);
		}

		public PageLayout GetPageLayoutById(string layoutId)
		{
			return PageLayouts.SingleOrDefault(pl => pl.Id == layoutId);
		}

		public SitemapNode FindSitemapNodeByPageId(string pageId)
		{
			// Return the primary reference.
			return this.AllSitemapNodes.Where(n => n.IsPrimaryPageReference && n.PageId == pageId).SingleOrDefault();
		}

		public IEnumerable<SitemapNode> FindSitemapNodesByPageId(string pageId)
		{
			return this.AllSitemapNodes.Where(n => n.PageId == pageId).ToArray();
		}

		public SitemapNode FindSitemapNodeById(string id)
		{
			return this.AllSitemapNodes.Where(n => n.Id == id).SingleOrDefault();
		}

		public void RemoveNodeFromSitemap(SitemapNode sitemapNode)
		{
			if (sitemapNode.Parent != null)
			{
				sitemapNode.Parent.SitemapNodes.Remove(sitemapNode);
			}
			else
			{
				this.SitemapNodes.Remove(sitemapNode);
			}
		}

		public void SetHomepage(SitemapNode sitemapNode)
		{
			var currentHomepage = AllSitemapNodes.Where(n => n.IsHomePage).SingleOrDefault();
			if (currentHomepage != null)
			{
				currentHomepage.IsHomePage = false;
			}
			sitemapNode.IsHomePage = true;
		}

		public void ChangeParentNode(string nodeId, string newParentNodeId)
		{
			var node = FindSitemapNodeById(nodeId);
			var oldParentNode = node.Parent;
			if (oldParentNode != null)
			{
				oldParentNode.SitemapNodes.Remove(node);
			}
			if (!String.IsNullOrEmpty(newParentNodeId))
			{
				var newParentNode = FindSitemapNodeById(newParentNodeId);
				newParentNode.SitemapNodes.Add(node);
				node.Parent = newParentNode;
			}
			else
			{
				SitemapNodes.Add(node);
				node.Parent = null;
			}
		}

		public void SortNodes(string parentNodeId, string[] orderedChildNodeIds)
		{
			var parentNode = FindSitemapNodeById(parentNodeId);
			if (parentNode != null)
			{
				var childNodeDictionary = parentNode.SitemapNodes.ToDictionary(n => n.Id, n => n);
				parentNode.SitemapNodes.Clear();
				foreach (var orderedChildNodeId in orderedChildNodeIds)
				{
					parentNode.SitemapNodes.Add(childNodeDictionary[orderedChildNodeId]);
				}
			}
			else
			{
				var childNodeDictionary = SitemapNodes.ToDictionary(n => n.Id, n => n);
				SitemapNodes.Clear();
				foreach (var orderedChildNodeId in orderedChildNodeIds)
				{
					SitemapNodes.Add(childNodeDictionary[orderedChildNodeId]);
				}
			}

		}

		public Theme GetThemeByName(string themeName)
		{
			return this.Themes.SingleOrDefault(t => t.Name == themeName);
		}

		public void InstallTheme(Theme theme)
		{
			if (this.Themes.Any(t => t.Name == theme.Name))
			{
				throw new ArgumentException(string.Format("A theme with name {0} is already installed in the site.", theme.Name));
			}
			this.Themes.Add(theme);
		}

		public void UninstallTheme(Theme theme)
		{
			if (this.DefaultTheme == theme.Name || this.PageLayouts.Any(pl => pl.Theme == theme.Name))
			{
				throw new ArgumentException(string.Format("The theme with name {0} cannot be uninstalled because it's being used by a site or a page layout.", theme.Name));
			}
			this.Themes.Remove(theme);
		}

		public string GetHomePageUrl()
		{
			var sitemapNode = this.SitemapNodes.FirstOrDefault(sn => sn.IsHomePage);
			if (sitemapNode != null)
			{
				return sitemapNode.Url;
			}
			return null;
		}

		public void InstallPageType(PageType pageType)
		{
			if (this.PageTypes.Any(t => t.Id == pageType.Id))
			{
				//throw new ArgumentException(string.Format("A page type with id {0} is already installed in the site.", pageType.Id));
			}
			else
			{
				this.PageTypes.Add(pageType);				
			}
		}

		public void UninstallPageType(PageType pageType)
		{
			if (this.PageLayouts.Any(pl => pl.PageType == pageType.Id))
			{
				throw new ArgumentException(string.Format("The page type with id {0} cannot be uninstalled because it's being used by a a page layout.", pageType.Id));
			}
			this.PageTypes.Remove(pageType);
		}

		public void InstallPluginType(PluginType pluginType)
		{
			if (this.PluginTypes.Any(t => t.Name == pluginType.Name))
			{
				throw new ArgumentException(string.Format("A plugin with name {0} is already installed in the site.", pluginType.Name));
			}
			this.PluginTypes.Add(pluginType);
		}

		public void UninstallPluginType(PluginType pluginType)
		{
			this.PluginTypes.Remove(pluginType);
		}
	}
}
